package com.zhuhjay.common.core.util;

import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.zhuhjay.common.base.expand.IpResolve;
import com.zhuhjay.common.base.util.SpringContextHolder;
import com.zhuhjay.common.redis.util.RedisUtils;
import com.zhuhjay.common.redis.util.RedissonUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;

import static com.zhuhjay.common.base.constant.TimeConstant.DAY;

/**
 * @author ZhuHJay
 * @date 2023/4/20 23:00
 */
@Slf4j
public class IpAddressUtils {

    /** 未知IP **/
    public static final String UNKNOWN = "? -未知IP- ?";

    private static final String IP_RESOLVE_FAIL_COUNT_PREFIX = "ip-resolve:count:";
    private static final String IP_RESOLVE_DISABLE_PREFIX = "ip-resolve:disable:";

    /**
     * 获取IP地址所在地
     * @param ip ip地址
     * @return 地址
     */
    public static String getRealAddressByIp(String ip) {
        // 内网不查询
        if (NetUtil.isInnerIP(ip)) {
            return "内网IP";
        }
        // 找到可用的ip解析方式
        for (IpResolve.IpResolveStrategy resolve : IpResolve.IpResolveStrategy.values()) {
            if (resolve.enable()) {
                // 是否被强制关停服务
                String disableKey = IP_RESOLVE_DISABLE_PREFIX + resolve.resolveName();
                if (RedisUtils.hasKey(disableKey)) {
                    log.warn("IP解析服务[{}]被强制关停", resolve.resolveName());
                    continue;
                }
                String result = resolve.resolve(ip);
                if (StrUtil.isNotBlank(result)) {
                    return result;
                }
                // 记录失败次数, 如果超过了一定次数, 就关停该解析方式(关停一段时间)
                else {
                    // 交给线程池完成这个任务
                    SpringContextHolder.getBean(ExecutorService.class).submit(() ->
                            RedissonUtils.withInfiniteWaitLock(disableKey, () -> {
                                String countKey = IP_RESOLVE_FAIL_COUNT_PREFIX + resolve.resolveName();
                                Long count = RedisUtils.increment(countKey);
                                if (count.intValue() == 1) {
                                    RedisUtils.setKeyExpireShort(countKey);
                                }
                                if (count.intValue() > 5) {
                                    RedisUtils.setCacheAndExpire(disableKey, "1", 5 * DAY);
                                    RedisUtils.deleteCache(countKey);
                                }
                            })
                    );
                }
            }
        }
        log.error("IP解析服务未解析: {}, 请检查解析服务是否正常！", ip);
        return UNKNOWN;
    }

}
